function defineReactive(data, key, value) {
  let dep = new Dep()
  Object.defineProperty(data, key, {
    enumerable: true, // 可枚举
    configurable: true, // 可修改对象属性
    get() {
      console.log('get', value)
      dep.depend()
      return value
    },
    set(newValue) {
      console.log('set', key, newValue)
      dep.notify()
      if (value === newValue) {
        return
      }
      value = newValue
    },
  })
}
const data = {
  title: 'vue 原理学习',
  time: '2022 06 25',
  author: 'zhou ping',
}
Object.keys(data).forEach((key) => {
  defineReactive(data, key, data[key])
})
data.title = '111'

class Dep {
  constructor() {
    this.subs = []
  }

  addSub(sub) {
    this.subs.push(sub)
  }

  removeSub(sub) {
    remove(this.subs, sub)
  }

  depend() {
    if (window.target) {
      this.addSub(window.target)
    }
  }

  notify() {
    const subs = this.subs.slice()

    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

function remove(arr, item) {
  if (arr.length) {
    const index = arr.indexOf(item)
    if (index > -1) {
      return arr.splice(index, 1)
    }
  }
}
class Watcher {
  constructor(vm, expOrFn, cb) {
    this.vm = vm
    this.getter = parsePath(expOrFn)
    this.cb = cb
    this.value = this.get()
  }

  get() {
    window.target = this
    let value = this.getter.call(this.vm, this.vm)
    window.target = undefined
    return value
  }

  update() {
    const oldValue = this.value
    this.value = this.get()
    this.cb.call(this.vm, this.value, oldValue)
  }
}

const bailRE = /[^\w.$]/
function parsePath(path) {
  if (bailRE.test(path)) {
    return
  }
  const segments = path.split('.')
  return function (obj) {
    for (let i = 0; i < segments.length; i++) {
      if (!obj) return
      obj = obj[segments[i]]
    }
    return obj
  }
}
